#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "psd_auth.h"
#include "psd_config.h"
#include "psd_logger.h"
#include "psd_modules.h"
#include "psd_auth_pass.h"

#include "compat.h"
#include "hashtable.h"

psdModule *module_info();
static int psd_file_auth_init();
static void psd_file_auth_read_config();
static psdPassUserInfo *file_get_user_info(const char *username);

struct psd_file_auth_config_t {
	const char *file;
};

static struct psd_file_auth_config_t psdFileAuthConfig = {
	.file = "passwd",
};

static psdPassBackend psd_file_pass_backend = {
	.name = "File",
	.get_user_info = file_get_user_info,
};

static psdModule psd_file_auth_module = {
	.desc = "File Password Authentication Module",
	.dependencies = "",
	.init = psd_file_auth_init
};

static struct hashtable *table;

psdModule *module_info() {
	return &psd_file_auth_module;
}

static void psd_file_auth_read_config() {
	char *str_val;

	if (psd_depot_settings_get("file_auth", "passwd_file", &str_val) == 0) {
		psdFileAuthConfig.file = str_val;
	}
}

static int psd_file_auth_htable_equal(const void *k1, const void *k2) {
	const char *c1 = k1;
	const char *c2 = k2;
	int i;

	i = 0;

	while(*c1 != '\0' && *c1 == *c2 && i < 1024) {
		i++;
	}

	if (i == 1024 || *c1 != *c2)
		return -1;

	return 0;
}

static unsigned int psd_file_auth_htable_hash(const void *k1) {
	const char *c = k1;
	unsigned int retval;
	int i;

	retval = 0;
	i = 0;

	while(*c != 0 && i < 1024) {
		retval += *c;
		i++;
		c++;
	}

	return retval;
}

int psd_file_auth_init() {
	FILE *f;
	char line[1024];
	int linenum;

	psd_file_auth_read_config();

	table = create_hashtable(7, psd_file_auth_htable_hash, psd_file_auth_htable_equal);
	if (!table) {
		psd_err(0, "couldn't create hash table");
		goto error_exit;
	}

	f = fopen(psdFileAuthConfig.file, "r");
	if (!f) {
		psd_err(0, "couldn't initialize open: %s", psdFileAuthConfig.file);
		goto error_exit2;
	}

	linenum = 0;
	while(fgets(line, sizeof(line), f) != NULL) {
		char **fields;
		int field_count;
		psdPassUserInfo *ui;

		linenum++;

		fields = split(line, ",", &field_count);
		if (!fields) {
			psd_err(0, "couldn't read line: %s:%d", psdFileAuthConfig.file, linenum);
			continue;
		}

		if (field_count != 5) {
			psd_err(0, "invalid line: %s:%d", psdFileAuthConfig.file, linenum);
			continue;
		}

		ui = psd_alloc_pass_user_info();
		if (!ui) {
			psd_err(0, "couldn't allocate space to hold line: %s:%d", psdFileAuthConfig.file, linenum);
			continue;
		}

		ui->username = fields[0];
		ui->password = fields[1];
		ui->email = fields[2];
		ui->institution = fields[3];
		ui->activated = atoi(fields[4]);

		free(fields[4]);
		free(fields);

		if (!hashtable_insert(table, strdup(ui->username), ui)) {
			psd_err(0, "couldn't add line %s:%d to the hashtable", psdFileAuthConfig.file, linenum);
			psd_free_pass_user_info(ui);
			continue;
		}

	}

	if (psd_set_pass_backend(&psd_file_pass_backend)) {
		psd_err(0, "couldn't register password backend");
		goto error_exit3;
	}

	return 0;

error_exit3:
	fclose(f);
error_exit2:
	hashtable_destroy(table, 1);
error_exit:
	return -1;
}

// internal functions
static psdPassUserInfo *file_get_user_info(const char *username) {
	psdPassUserInfo *ret_ui, *ui;

	ui = hashtable_search(table, username);
	if (!ui)
		goto error_exit;

	ret_ui = psd_alloc_pass_user_info();
	if (!ret_ui)
		goto error_exit;

	ret_ui->username = strdup(ui->username);
	ret_ui->password = strdup(ui->password);
	ret_ui->email = strdup(ui->email);
	ret_ui->institution = strdup(ui->institution);
	ret_ui->activated = ui->activated;

	if (ret_ui->username == NULL || ret_ui->password == NULL || ret_ui->email == NULL || ret_ui->institution == NULL)
		goto error_exit2;

	return ret_ui;

error_exit2:
	psd_free_pass_user_info(ret_ui);
error_exit:
	return NULL;
}
